Skip to content

Add the BitConverter Half members - #600

Merged
SimonCropp merged 1 commit into
mainfrom
bitconverter-half
Sep 10, 2026
Merged

Add the BitConverter Half members#600
SimonCropp merged 1 commit into
mainfrom
bitconverter-half

Conversation

@SimonCropp

Copy link
Copy Markdown
Owner

Eight members: GetBytes(Half), HalfToInt16Bits, HalfToUInt16Bits, Int16BitsToHalf, UInt16BitsToHalf, ToHalf over an array and over a span, and TryWriteBytes(Span<byte>, Half).

The window really is net5.0 alone

I flagged this as poor value in #598 and should be precise about why, because it is narrower than it looks:

  • Half the type is net5.0.
  • Every one of these BitConverter members is net6.0 — confirmed absent from the 5.0.0 ref pack and present in 6.0.36.

So below net5.0 there is nothing to convert, and from net6.0 the BCL supplies them. One target framework. Each member carries a //Note: saying so, the same way the Int128 members do, since the #### BitConverter section header gives no hint of a floor.

Narrow, but the value is real for anyone still on net5.0: today they have no way to get at a Half's bits at all, because the type deliberately exposes none.

Reinterpretation, not conversion

The obvious shortcut — round through float — silently destroys NaN payloads and would produce a canonical NaN for every one of the 2046 distinct NaN encodings. So the bits go through MemoryMarshal over a two-byte stack buffer, which is exact and allocation-free (GetBytes allocates its 2-byte array, as the BCL does). No unsafe code and no dependency on Unsafe.

Endianness cancels out for the *Bits* members: writing a Half in platform order and reading a short back in platform order yields the same sixteen bits either way.

Verified exhaustively

Half is 16 bits, so this does not need sampling. All 65 536 patterns were swept against the BCL before the code was written, and the shipped tests do the same sweep: every pattern round trips, and GetBytes, ToHalf and TryWriteBytes agree with the BCL byte for byte. Zero mismatches, including every NaN payload, both infinities, negative zero, and the subnormal boundary. Nine notable patterns are also pinned explicitly, so a regression names the case rather than just a count.

Argument validation matches the array overloads already on the type — ArgumentNullException on null, ArgumentOutOfRangeException on startIndex outside the array, and ArgumentException naming value when the index is in range but only one byte remains.

How the polyfill was actually exercised

This is the awkward part, and worth stating plainly: the polyfill compiles only on net5.0, and that runtime is not installed on this machine, so a normal test run would have exercised the BCL and never the polyfill.

So the net5.0 test build was run against the net11 runtime with dotnet exec --roll-forward LatestMajor. That build compiling at all already proves the polyfill is doing the work — net5.0 has none of these members, so without it there would be nothing to bind to — and all 1655 tests pass there, including the exhaustive sweep. CI, which has the net5.0 runtime, will run it natively.

Result

API count 1139 → 1147.

Solution clean in Release, Consume clean across all 22 TFMs, tests green on net11.0 (1714), net10.0 (1714), net9.0 (1714), net8.0 (1711), net462 (1665), net5.0 via roll-forward (1655), plus PublicTests, EmbeddedTests, UnsafeTests, NoRefsTests and NoExtrasTests.

Eight members: GetBytes, HalfToInt16Bits, HalfToUInt16Bits, Int16BitsToHalf,
UInt16BitsToHalf, ToHalf over an array and over a span, and TryWriteBytes.

The window is net5.0 alone. Half arrived in net5.0 but every one of these BitConverter
members is net6.0, so below net5.0 there is no type to convert and from net6.0 the BCL
has them. Noted on each, since the section header does not show it.

Reinterpretation goes through MemoryMarshal over a two byte stack buffer rather than
through float, which would lose NaN payloads. Verified exhaustively: all 65536 bit
patterns round trip and agree with the BCL byte for byte, including every NaN payload,
both infinities, negative zero and the subnormal boundary.

The polyfill only compiles on net5.0, whose runtime is not installed here, so the
net5.0 test build was run on the net11 runtime with roll forward to exercise it. That
build compiling at all already requires the polyfill, since net5.0 has none of these
members, and all 1655 tests pass there.

API count 1139 -> 1147.
@SimonCropp
SimonCropp merged commit a5862c1 into main Sep 10, 2026
5 checks passed
@SimonCropp
SimonCropp deleted the bitconverter-half branch September 10, 2026 08:06
@SimonCropp SimonCropp added this to the 11.3.0 milestone Sep 10, 2026
This was referenced Sep 10, 2026
This was referenced Sep 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant